package com.adjaisd.scarecrow.controller;


import com.adjaisd.scarecrow.common.exception.VerifyCodeException;
import com.adjaisd.scarecrow.common.result.Result;
import com.adjaisd.scarecrow.common.result.ResultCode;
import com.adjaisd.scarecrow.common.utils.jwt.JwtUtils;
import com.adjaisd.scarecrow.common.utils.RandomUtils;
import com.adjaisd.scarecrow.common.utils.verify.IVerifyCodeGen;
import com.adjaisd.scarecrow.common.utils.verify.SimpleCharVerifyCodeGenImpl;
import com.adjaisd.scarecrow.common.utils.verify.VerifyCode;
import com.adjaisd.scarecrow.entity.Mail;
import com.adjaisd.scarecrow.entity.OssToken;
import com.adjaisd.scarecrow.entity.User;
import com.adjaisd.scarecrow.service.*;
import io.jsonwebtoken.Claims;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.Date;
import java.util.LinkedList;
import java.util.Map;

/**
 * <p>
 * 前端控制器
 * </p>
 *
 * @author adjaisd
 * @since 2022-03-05
 */
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    // 获取验证码， get: /user/verify
    @ApiOperation(value = "获取验证码")
    @RequestMapping(value = "/verify", method = RequestMethod.GET,
            produces = {MediaType.IMAGE_JPEG_VALUE, MediaType.IMAGE_PNG_VALUE})
    public void verifyCode(HttpServletRequest request, HttpServletResponse response) {
        HttpSession session = request.getSession();
        IVerifyCodeGen iVerifyCodeGen = new SimpleCharVerifyCodeGenImpl();
        try {
            VerifyCode verifyCode = iVerifyCodeGen.generate(80, 28);//设置长宽
            String code = verifyCode.getCode();
            response.setHeader("Pragma", "no-cache");   //设置响应头
            response.setHeader("Cache-Control", "no-cache");//设置响应头
            response.setDateHeader("Expires", 0); //在代理服务器端防止缓冲
            response.setContentType("image/jpeg");  //设置响应内容类型
            response.getOutputStream().write(verifyCode.getImgBytes());
            session.setAttribute("VerifyCode", code);// 存储验证码
            response.getOutputStream().flush();
        } catch (IOException e) {
            throw new VerifyCodeException(ResultCode.ERROR, e.getMessage());
        }
    }

    @Autowired
    private MailService mailService;

    @ApiOperation(value = "发送邮箱验证码")
    @ApiImplicitParam(name = "mail", required = true, value = "mail对象", dataType = "Mail")
    @RequestMapping(value = "/mail", method = RequestMethod.POST)
    @ResponseBody
    public Result sendMail(@RequestBody Mail mail, HttpServletRequest request) {
        HttpSession session = request.getSession();
        String mailVerifyCode = RandomUtils.randomNumberString(5);
        String to = mail.getTo();
        String subject = "IntelliScarecrow帐号验证码";
        String content = "\n" +
                "尊敬的用户 ，您好！\n" +
                "为确保是您本人操作，通过该邮件地址获取验证码验证身份。请在邮件验证码输入框输入下方验证码：\n" +
                "\n" +
                mailVerifyCode + "\n" +
                "\n" +
                "勿向任何人泄露您收到的验证码。";
        mailService.sendSimpleMail(to, subject, content);
        session.setAttribute("mail-verify-code", mailVerifyCode);
        return Result.succ().code(200).message("发送验证码成功");
    }

    // 注册： post: /user/register
    @ApiOperation(value = "注册")
    @RequestMapping(value = "/register", method = RequestMethod.POST)
    @ResponseBody
    public Result register(HttpServletRequest request, @RequestBody Map<String, String> registerMap) {
        String username = registerMap.get("username");
        // 是否存在用户名
        boolean existUsername = userService.existUserByUsername(username);
        if (existUsername) return Result.fail()
                .Code(500)
                .message("用户名已存在.");
        String password = registerMap.get("password");
        String email = registerMap.get("email");

        String verifyCode = (String) request.getSession().getAttribute("mail-verify-code");
        String inputCode = registerMap.get("verifyCode");
        boolean verify = inputCode.equalsIgnoreCase(verifyCode); // 验证码是否正确
        if (!verify) return Result.fail()
                .Code(500)
                .message("验证码输入不正确.");

        Long createDate = new Date().getTime();
        User user = new User();
        user.setUsername(username);
        user.setPassword(password);
        user.setEmail(email);
        user.setIsSuper(false);
        user.setCreateDate(createDate);
        user.setSalt(RandomUtils.randomString(14));

        int i = userService.addNewUser(user);
        if (i > 0)
            return Result.succ().message("注册成功.");
        else
            return Result.fail().code(500).message("注册失败.");
    }

    @Autowired
    RedisService redisService;

    @ApiOperation(value = "登陆")
    @ApiImplicitParam(name = "loginForm", required = true, value = "用户登录信息", paramType = "body", dataType = "json")
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    @ResponseBody
    public Result login(HttpServletRequest request, @RequestBody Map<String, String> loginForm) {
        String verifyCode = (String) request.getSession().getAttribute("VerifyCode");
        String inputCode = loginForm.get("code");
        boolean verify = inputCode.equalsIgnoreCase(verifyCode);  // 验证码是否正确
        if (!verify) return Result.fail().Code(500).message("验证码错误");

        String username = loginForm.get("username");
        String password = loginForm.get("password");
        if (!userService.existUser(username, password)) return Result.fail()
                .Code(500)
                .message("用户名或密码不正确");
        User user = userService.getUserByUsername(username);
        Long id = user.getId();
        String key = "token" + id;

        // 生成token
        String jwtToken = JwtUtils.getJwtToken(id, username, password, user.getSalt());
        request.getSession().setAttribute("token", jwtToken);

        redisService.set(key, jwtToken, 5);

        return Result.succ().code(20000)
                .data("id", id)
                .data("username", username)
                .data("token", jwtToken)
                .message("登陆成功");
    }

    // 用户信息: post: /user/info
    @ApiOperation(value = "查询用户信息")
    @RequestMapping(value = "/info", method = RequestMethod.GET)
    @ResponseBody
    public Result getUserinfo(HttpServletRequest request) {
        String token = request.getHeader("X-token");
        Claims claims = JwtUtils.getMembers(token);
        String username = (String) claims.get("username");
        User user = userService.getUserByUsername(username);

        LinkedList<String> roles = new LinkedList<>();
        if (user.getIsSuper()) roles.push("admin");
        else roles.push("editor");
        String avatar = user.getAvatar();
        if (avatar == null || avatar.length() == 0) avatar = "https://sc-avatar.oss-cn-beijing.aliyuncs.com/avatar/avatar.png";
        return Result.succ()
                .Code(20000)
                .data("roles", roles)
                .data("name", username)
                .data("avatar", avatar)
                .message("查询个人信息成功");
    }

    // 退出登陆： post: /user/logout
    @ApiOperation(value = "退出登陆")
    @RequestMapping(value = "/logout", method = RequestMethod.POST)
    @ResponseBody
    public Result logout(HttpServletRequest request, HttpServletResponse response) {
        String token = request.getHeader("X-token");
        if (!JwtUtils.checkToken(token)) {
            response.setStatus(403);
            return Result.fail().code(50008).message("用户未登录");
        }
        Claims info = JwtUtils.getMembers(token);
        int id = (int) info.get("id");
        String key = "token" + id;

        if (!redisService.existKey(key) ||      // 不存在key
                redisService.isOverdue(key) ||  // key过期
                !redisService.equalVal(key, token)) { // token与val不等
            response.setStatus(403);
            return Result.fail().code(50008).message("用户未登录");
        }
        if (redisService.delete(key)) return Result.succ().code(20000).message("成功退出登陆");
        else return Result.fail().code(50000).message("服务器繁忙");
    }

    // 保持会话连接: get: /user/keep
    @ApiOperation(value = "获取STS令牌")
    @RequestMapping(value = "/keep", method = RequestMethod.GET)
    @ResponseBody
    public Result keepConnect(HttpServletRequest request) {
        String token = request.getHeader("X-token");
        Claims claims = JwtUtils.getMembers(token);
        long userId = Long.parseLong(claims.get("id").toString());
        String key = "token" + userId;
        Long expire = redisService.getExpire(key);
        return Result.succ().code(200)
                .data("expire", expire)
                .message("更新会话");
    }


    @Autowired
    OssTokenService ossTokenService;

    // 获取STS令牌: get: /user/sts
    @ApiOperation(value = "获取STS令牌")
    @RequestMapping(value = "/sts", method = RequestMethod.GET)
    @ResponseBody
    public Result getOssToken(HttpServletRequest request) {
        String token = request.getHeader("X-token");
        Claims claims = JwtUtils.getMembers(token);
        long userId = Long.parseLong(claims.get("id").toString());
        OssToken stsToken = ossTokenService.getToken();
        return Result.succ().code(200)
                .data("stsToken", stsToken)
                .data("userId", userId)
                .message("获取stsToken成功");
    }

    // -------测试用----------------


    @ApiOperation(value = "登陆")
    @ApiImplicitParam(name = "loginForm", required = true, value = "用户登录信息", paramType = "body", dataType = "json")
    @RequestMapping(value = "/nologin", method = RequestMethod.POST)
    @ResponseBody
    public Result loginWithoutAuth(HttpServletRequest request, @RequestBody Map<String, String> loginForm) {
        String username = loginForm.get("username");
        String password = loginForm.get("password");
        if (!userService.existUser(username, password)) return Result.fail()
                .Code(500)
                .message("用户名或密码不正确");
        User user = userService.getUserByUsername(username);
        Long id = user.getId();
        String key = "token" + id;

        // 生成token
        String jwtToken = JwtUtils.getJwtToken(id, username, password, user.getSalt());
        request.getSession().setAttribute("token", jwtToken);

        redisService.set(key, jwtToken, 5);

        return Result.succ().code(20000)
                .data("id", id)
                .data("username", username)
                .data("token", jwtToken)
                .message("登陆成功");
    }

}
